#include <stdlib.h>
#include "Log.h"
#include "Sys_Def.h"
#include "UART_Ctrl.h"
#include "UART_Com.h"
#include "UART_Upgrade.h"
#include "Systick_Ctrl.h"
#include "cdc_acm_core.h"
#include "VCOM_Upgrade.h"

uint8_t  gComCmd[COM_MSG_SIZE + 1];
uint8_t  gVComCmd[COM_MSG_SIZE + 1];
uint8_t  gComMsgBuf[COM_PACKET_SIZE];
uint32_t gComMsgWIdx = 0;
uint32_t gComMsgRIdx = 0;

void UART_ComStart(void)
{
    UART_DisableDMARX(COM_UART);
    UART_EnableRxIRQ(COM_UART);
}

void UART_ComSaveByte(uint8_t RcvByte)
{
    uint32_t NewWIdx = ((gComMsgWIdx + 1) & (COM_BUF_SIZE - 1));

    if(NewWIdx == gComMsgRIdx)
    {
        /*Buffer is full*/
        return;
    }

    gComMsgBuf[gComMsgWIdx] = RcvByte;
    gComMsgWIdx = NewWIdx;
}

int UART_ComGetBytes(uint8_t *pByte, uint32_t len)
{
    uint32_t NewRIdx;
    uint32_t i = 0;

    if(gComMsgWIdx == gComMsgRIdx)
    {
        return -1;
    }

    NewRIdx = gComMsgRIdx;
    while(NewRIdx != gComMsgWIdx && i < len)
    {
        pByte[i] = gComMsgBuf[NewRIdx];
        i++;
        NewRIdx = ((NewRIdx + 1) & (COM_BUF_SIZE - 1));
    }

    if(i == len)
    {
        gComMsgRIdx = NewRIdx;
        return i;
    }

    return 0;
}

static int VCOM_RcvCmdPacket(uint8_t* pData, int WantLen)
{
#define MAX_WAIT_TIMES 10
    int WaitTimes = 0;
    int CurRcvlen = 0;
    int TotalRcvlen = 0;

    uint8_t *pCurBuf = pData;    
    while(TotalRcvlen < WantLen && WaitTimes < MAX_WAIT_TIMES)
    {
        CurRcvlen = cdc_acm_data_receive(&cdc_acm, pCurBuf, WantLen - TotalRcvlen);
        if(CurRcvlen > 0)
        {
            pCurBuf += CurRcvlen;
            TotalRcvlen += CurRcvlen;
        }
        CurRcvlen = 0;
        WaitTimes++;
        Systick_Delay(10);
    }
    
    return TotalRcvlen;
}


void UART_ComProc(void)
{
    int flag = 1;
    int uartMsgLen = 0;
    int vcomMsgLen = 0;
    uint8_t reboot[6] = {'R', 'e', 'b', 'o', 'o', 't'};

    do {
        gComCmd[0] = 0;
        uartMsgLen = UART_ComGetBytes(gComCmd, 7);
        gComCmd[COM_MSG_SIZE] = 0;
        if (uartMsgLen > 0) {
            LOG_PRINT(("Got UART Message: %s\r\n", gComCmd));
            if(gComCmd[0] == 'U' && gComCmd[1] == 'p' && gComCmd[2] == 'g' &&
               gComCmd[3] == 'r' && gComCmd[4] == 'a' && gComCmd[5] == 'd' && gComCmd[6] == 'e')
            {
                UART_Send(COM_UART, reboot, 6);
                UART_ConfigForUpgrade(UPGRADE_UART);
                LOG_PRINT(("Will Reboot for UART upgrade!\r\n"));
                fmc_unlock();
                fmc_word_program(USER_DATA_ADDR, ONLINE_UART_UPGRADE_FLAG);
                fmc_lock();
                iap_load_app(FMC_START_ADDR);
            }
        }

        gVComCmd[0] = 0;
        vcomMsgLen = VCOM_RcvCmdPacket(gVComCmd, COM_MSG_SIZE);
        gVComCmd[COM_MSG_SIZE] = 0;
        if (vcomMsgLen > 0) {
            LOG_PRINT(("Got VCOM Message: %s\r\n", gVComCmd));
            if(gVComCmd[0] == 'U' && gVComCmd[1] == 'p' && gVComCmd[2] == 'g' &&
               gVComCmd[3] == 'r' && gVComCmd[4] == 'a' && gVComCmd[5] == 'd' && gVComCmd[6] == 'e')
            {
                cdc_acm_data_send_data(&cdc_acm, reboot, 6);
                Systick_Delay(1000);
                VCOM_RcvCmdPacket(gVComCmd, COM_MSG_SIZE);
                UART_ConfigForUpgrade(UPGRADE_UART);
                LOG_PRINT(("Will Reboot for VCOM upgrade!\r\n"));
                fmc_unlock();
                fmc_word_program(USER_DATA_ADDR, ONLINE_VCOM_UPGRADE_FLAG);
                fmc_lock();
                Systick_Delay(2000);
                iap_load_app(FMC_START_ADDR);
            }
        }

        if(uartMsgLen <= 0 && vcomMsgLen <= 0) {
            flag = 0;
        }
    }while(flag);
}

